﻿/*===================================================
* CLR版本  : 4.0.30319.42000 
* 类名称   : ObjectEvent
* 文 件 名 : ObjectEvent 
* 类描述   :
* 创建人   : oream
* 创建时间 : 2018/3/7 17:19:48
* 修改人   : 
* 修改时间 :
* 版本： @version 1.0
=====================================================*/

using Model;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;

namespace Hotfix
{
  public class ObjectSystem<T> : IObjectSystem
  {
    private T value;

    public void Set(object value)
    {
      this.value = (T)value;
    }

    public Type Type()
    {
      return typeof(T);
    }

    public T Get()
    {
      return this.value;
    }
  }


  public class ObjectEvent
  {
    private Dictionary<Type, IObjectSystem> dicObjectStstem = new Dictionary<Type, IObjectSystem>();
    private Dictionary<ushort, List<Hotfix.IEvent>> dicEventDir = new Dictionary<ushort, List<Hotfix.IEvent>>();

    private Queue<Disposer> qStart = new Queue<Disposer>();

    public Queue<Disposer> qUpdate = new Queue<Disposer>();
    public Queue<Disposer> qUpdate2 = new Queue<Disposer>();

    public Queue<Disposer> qLateUpdate = new Queue<Disposer>();
    public Queue<Disposer> qLateUpdate2 = new Queue<Disposer>();



    public ObjectEvent()
    {
      Type[] types = DllHelper.GetAllType();
      foreach (var type in types)
      {
        object[] tmpObj = type.GetCustomAttributes(typeof(ObjectSystemAttribute), false);
        if (tmpObj.Length == 0) continue;
        if (!(tmpObj[0] is ObjectSystemAttribute)) continue;
        object tmp = Activator.CreateInstance(type);
        IObjectSystem objectSystem = tmp as IObjectSystem;
        if (objectSystem == null)
        {
          Debug.LogError($"组件{type.Name}没有继承IObjectSystem");
        }
        this.dicObjectStstem.Add(objectSystem.GetType(), objectSystem);
      }

      foreach (var type in types)
      {
        object[] tmpObj = type.GetCustomAttributes(typeof(EventAttribute), false);
        if (tmpObj.Length == 0) continue;
        foreach (var item in tmpObj)
        {
          if (!(item is EventAttribute)) continue;
          EventAttribute eventAttribute = item as EventAttribute;
          if (eventAttribute == null) continue;
          object tmp = Activator.CreateInstance(type);
          Hotfix.IEvent tmpEvnet = tmp as Hotfix.IEvent;
          if (tmpEvnet == null)
          {
            Debug.LogWarning($"{type.Name}没有继承IEvnet , 无法执行事件系统,请检查!");
          }
          this.RegisterEvent(eventAttribute.type, tmpEvnet);

          Action<List<object>> tmpAction = list => { this.Handle(eventAttribute.type, list); };
          Game.EventSystem.RegisterEvent(eventAttribute.type, new EventProxy(tmpAction));
        }

      }
    }


    #region Event

    private void Handle(ushort type, List<object> param)
    {
      switch (param.Count)
      {
        case 0:
          this.Run(type);
          break; ;
        case 1:
          this.Run(type, param[0]);
          break;
        case 2:
          this.Run(type, param[0], param[1]);
          break;
        case 3:
          this.Run(type, param[0], param[1], param[2]);
          break;
        default:
          break;
      }

    }

    private void RegisterEvent(ushort eventType, Hotfix.IEvent eventInfo)
    {
      if (!this.dicEventDir.ContainsKey(eventType))
      {
        List<Hotfix.IEvent> tmpList = new List<IEvent>();
        this.dicEventDir.Add(eventType, tmpList);
      }
      this.dicEventDir[eventType].Add(eventInfo);
    }

    public void Run(ushort eventObject)
    {
      if (!this.dicEventDir.ContainsKey(eventObject)) return;
      foreach (var item in this.dicEventDir[eventObject])
      {
        try
        {
          item?.Handle();
        }
        catch (Exception e)
        {
          Debug.LogError(e.ToString());
        }
      }
    }

    public void Run<T>(ushort eventObject, T data)
    {
      if (!this.dicEventDir.ContainsKey(eventObject)) return;
      foreach (var item in this.dicEventDir[eventObject])
      {
        try
        {
          item?.Handle(data);
        }
        catch (Exception e)
        {
          Debug.LogError(e.ToString());
        }
      }
    }

    public void Run<T1, T2>(ushort eventObject, T1 data1, T2 data2)
    {
      if (!this.dicEventDir.ContainsKey(eventObject)) return;
      foreach (var item in this.dicEventDir[eventObject])
      {
        try
        {
          item?.Handle(data1, data2);
        }
        catch (Exception e)
        {
          Debug.LogError(e.ToString());
        }
      }
    }

    public void Run<T1, T2, T3>(ushort eventObject, T1 data1, T2 data2, T3 data3)
    {
      if (!this.dicEventDir.ContainsKey(eventObject)) return;
      foreach (var item in this.dicEventDir[eventObject])
      {
        try
        {
          item?.Handle(data1, data2, data3);
        }
        catch (Exception e)
        {
          Debug.LogError(e.ToString());
        }
      }
    }

    #endregion


    private void Add(Disposer disposer)
    {
      IObjectSystem objectSystem;
      if (!this.dicObjectStstem.TryGetValue(disposer.GetType(), out objectSystem))
      {
        return;
      }
      if (objectSystem is IStart)
      {
        this.qStart.Enqueue(disposer);
      }
      if (objectSystem is IUpdate)
      {
        this.qUpdate.Enqueue(disposer);
      }
      if (objectSystem is ILateUpdate)
      {
        this.qLateUpdate.Enqueue(disposer);
      }

    }

    public void Awake(Disposer disposer)
    {
      this.Add(disposer);
      IObjectSystem objectSystem;
      if (!this.dicObjectStstem.TryGetValue(disposer.GetType(), out objectSystem))
      {
        throw new Exception($"{disposer.GetType().Name} Not Find");
      }
      IAwake awake = objectSystem as IAwake;
      if (awake == null)
      {
        throw new Exception($"{disposer.GetType().Name} Not Inherit IAwake");
      }
      objectSystem.Set(disposer);
      awake.Awake();

    }

    public void Awake<T>(Disposer disposer, T t)
    {
      this.Add(disposer);
      IObjectSystem objectSystem;
      if (!this.dicObjectStstem.TryGetValue(disposer.GetType(), out objectSystem)) throw new Exception($"{disposer.GetType().Name} Not Find");
      IAwake<T> awake = objectSystem as IAwake<T>;
      if (awake == null) throw new Exception($"{disposer.GetType().Name} Not Inherit IAwake");
      objectSystem.Set(disposer);
      awake.Awake(t);

    }

    public void Awake<T1, T2>(Disposer disposer, T1 t1, T2 t2)
    {
      this.Add(disposer);
      IObjectSystem objectSystem;
      if (!this.dicObjectStstem.TryGetValue(disposer.GetType(), out objectSystem)) throw new Exception($"{disposer.GetType().Name} Not Find");
      IAwake<T1, T2> awake = objectSystem as IAwake<T1, T2>;
      if (awake == null) throw new Exception($"{disposer.GetType().Name} Not Inherit IAwake");
      objectSystem.Set(disposer);
      awake.Awake(t1, t2);
    }

    public void Awake<T1, T2, T3>(Disposer disposer, T1 t1, T2 t2, T3 t3)
    {
      this.Add(disposer);
      IObjectSystem objectSystem;
      if (!this.dicObjectStstem.TryGetValue(disposer.GetType(), out objectSystem)) throw new Exception($"{disposer.GetType().Name} Not Find");
      IAwake<T1, T2, T3> awake = objectSystem as IAwake<T1, T2, T3>;
      if (awake == null) throw new Exception($"{disposer.GetType().Name} Not Inherit IAwake");
      objectSystem.Set(disposer);
      awake.Awake(t1, t2, t3);

    }

    public void Start()
    {
      while (this.qStart.Count > 0)
      {
        Disposer disposer = qStart.Dequeue();
        if (disposer.Id == 0) continue;
        IObjectSystem objectSystem;
        if (!this.dicObjectStstem.TryGetValue(disposer.GetType(), out objectSystem)) continue;
        IStart start = objectSystem as IStart;
        if (start == null) continue;
        objectSystem.Set(disposer);
        start.Start();
      }
    }

    public void Update()
    {
      this.Start();
      while (this.qUpdate.Count > 0)
      {
        Disposer disposer = this.qUpdate.Dequeue();
        IObjectSystem objectSystem;
        if (!this.dicObjectStstem.TryGetValue(disposer.GetType(), out objectSystem)) continue;
        IUpdate update = objectSystem as IUpdate;
        if (update == null) continue;
        this.qUpdate2.Enqueue(disposer);
        objectSystem.Set(disposer);
        try
        {
          update.Update();
        }
        catch (Exception e)
        {
          Debug.LogError(e.ToString());
          throw new Exception(e.ToString()); ;
        }
      }
      ObjectHelper.Swap(ref qUpdate, ref qUpdate2);
    }

    public void LateUpdate()
    {
      while (this.qLateUpdate.Count > 0)
      {
        Disposer disposer = this.qLateUpdate.Dequeue();
        IObjectSystem objectSystem;
        if (!this.dicObjectStstem.TryGetValue(disposer.GetType(), out objectSystem)) continue;
        ILateUpdate lateUpdate = objectSystem as ILateUpdate;
        if (lateUpdate == null) continue;
        objectSystem.Set(disposer);
        this.qLateUpdate2.Enqueue(disposer);
        try
        {
          lateUpdate.LateUpdate();
        }
        catch (Exception e)
        {
          Debug.LogError(e.ToString());
          throw new Exception(e.ToString());
        }
      }
      ObjectHelper.Swap(ref qLateUpdate, ref qLateUpdate2);
    }

  }
}
